extends VBoxContainer

signal channel_pressed
signal initialized

export(PackedScene) var Comment_scene: PackedScene = load("res://src/scenes/comments/comment/comment.tscn")

const PAGE_SIZE: int = 20
# The index of the option is what should be used when fetching comments
const SORT_OPTIONS: Array = [
	"Newest",
	"Oldest",
	"Controversy",
	"Popularity",
	"Newest no pins"
]

var _claim_id: String
# Total number of top level comments
var _total_comments: int = -1
var _page: int = 1
# State this scene should be initialized with
var _init_state: Dictionary = {}
var _initialized: int = false
var _async_helper := AsyncHelper.new(self)
onready var _comments_container: VBoxContainer = find_node("Comments")
onready var _show_more_comments_button: Button = \
		find_node("ShowMoreCommentsButton")


func init(claim_id: String) -> void:
	if claim_id != "":
		_claim_id = claim_id
		_initialized = true
		emit_signal("initialized")


func _ready() -> void:
	if not _init_state.empty():
		init(_init_state["_claim_id"])
	if not _initialized:
		yield(self, "initialized")
	
	_show_more_comments_button.connect(
		"pressed", self, "_on_ShowMoreCommentsButton_pressed")
	
	if _init_state.empty():
		_add_comments(yield(_compile_comments_data(), "completed"))
	else:
		_setup_state()


func get_state() -> Dictionary:
	"""
	Returns:
		The data needed to restore the main changes made to this scene
	"""
	var state: Dictionary = {
		"_claim_id": _claim_id,
		"sort_by": _get_selected_sort_option(),
	}
	return state


func set_state(state: Dictionary) -> void:
	"""
	Call before the node enters the SceneTree to initialize it with the given
	state
	"""
	_init_state = state


func _setup_state() -> void:
	""" Sets up the scene using the state description in `_init_state` """
	_add_comments(yield(_compile_comments_data(), "completed"))


func _compile_comments_data() -> Array:
	""" Gets a page of top level comments and their reactions """
	var get_comments_args: Dictionary = {
		"claim_id": _claim_id,
		"top_level": true,
		"sort_by": _get_selected_sort_option(),
		"page": _page,
		"page_size": PAGE_SIZE,
	}
	var tracker: Dictionary = _async_helper.connect_wrapped(
			Requests.get_comments(get_comments_args), "completed")
	yield(_async_helper.wait_until_finished([tracker]), "completed")
	var get_comments_response: Dictionary = tracker["result"]
	var comments_data: Array = get_comments_response.get("items", [])
	_total_comments = get_comments_response.get("total_filtered_items", 0)
	_page += 1
	
	var comment_ids := PoolStringArray()
	for comment_data in comments_data:
		comment_ids.append(comment_data.get("comment_id", ""))
	
	var get_reactions_args: Dictionary = {
		"comment_ids": comment_ids.join(",")
	}
	var comments_reactions: Dictionary = \
			yield(_get_reactions(get_reactions_args), "completed")
	
	for i in range(len(comments_data)):
		var id: String = comments_data[i].get("comment_id", "")
		comments_data[i]["others_reactions"] = comments_reactions.get(id, {})
	
	return comments_data


func _get_reactions(args: Dictionary) -> Dictionary:
	var tracker: Dictionary = _async_helper.connect_wrapped(
			Requests.get_comments_reactions(args), "completed")
	yield(_async_helper.wait_until_finished([tracker]), "completed")
	var reactions_data: Dictionary = tracker["result"]
	return reactions_data.get("others_reactions", {})


func _add_comments(comments_data: Array, parent_comment: Comment=null) -> void:
	"""
	Adds the comments within `comments_data` as replies to `parent_comment`. If
	`parent_comment == null` then adds them as root level comments
	
	Args:
		comments_data: array of the required data to create Comment objects
		parent_comment: the comment that the given comments are replies to.
			`null` if the given comments are root level comments
	"""
	if _total_comments == 0:
		var label := Label.new()
		label.text = "No comments"
		_comments_container.hide()
		add_child(label)
		return
	
	if parent_comment == null:
		for comment_data in comments_data:
			var comment_node: Comment = Comment_scene.instance()
			comment_node.init(comment_data)
			comment_node.connect("channel_pressed", self,
					"_on_Comment_channel_pressed")
			comment_node.connect("wants_replies", self,
					"_on_Comment_wants_replies", [comment_node])
			_comments_container.add_child(comment_node)
		
		# Hide ShowMoreCommentsButton if all comments have been loaded
		_show_more_comments_button.set_disabled(false)
		_show_more_comments_button.set_visible(
				_comments_container.get_child_count() < _total_comments)
	else:
		var comment_nodes: Array = []
		for comment_data in comments_data:
			var comment_node: Comment = Comment_scene.instance()
			comment_node.init(comment_data)
			comment_node.connect("channel_pressed", self,
					"_on_Comment_channel_pressed")
			comment_node.connect("wants_replies", self,
					"_on_Comment_wants_replies", [comment_node])
			comment_nodes.append(comment_node)
		parent_comment.add_replies(comment_nodes)


func _get_selected_sort_option() -> int:
	# TODO: Implement sort options in the UI and retrieve from there. Also
	# apply given sort option when initializing from a state
	return 3


func _on_ShowMoreCommentsButton_pressed() -> void:
	_show_more_comments_button.set_disabled(true)
	_add_comments(yield(_compile_comments_data(), "completed"))


func _on_Comment_channel_pressed(channel_url: String) -> void:
	if channel_url != "":
		emit_signal("channel_pressed", channel_url)


func _on_Comment_wants_replies(comment_node: Comment) -> void:
	var comment_id: String = comment_node.get_comment_id()
	var page: int = comment_node.get_last_replies_page_loaded() + 1
	var get_comments_args: Dictionary = {
		"claim_id": _claim_id,
		"parent_id": comment_id,
		"sort_by": _get_selected_sort_option(),
		"page": page,
		"page_size": PAGE_SIZE,
	}
	var tracker: Dictionary = _async_helper.connect_wrapped(
			Requests.get_comments(get_comments_args), "completed")
	yield(_async_helper.wait_until_finished([tracker]), "completed")
	var get_comments_response: Dictionary = tracker["result"]
	var comments_data: Array = get_comments_response.get("items", [])
	comment_node.increment_last_replies_page_loaded()
	
	var comment_ids := PoolStringArray()
	for comment_data in comments_data:
		comment_ids.append(comment_data.get("comment_id", ""))
	
	var get_reactions_args: Dictionary = {
		"comment_ids": comment_ids.join(",")
	}
	var comments_reactions: Dictionary = \
			yield(_get_reactions(get_reactions_args), "completed")
	
	for i in range(len(comments_data)):
		var id: String = comments_data[i].get("comment_id", "")
		comments_data[i]["others_reactions"] = comments_reactions.get(id, {})
	
	_add_comments(comments_data, comment_node)
